<?php

	/**
	 * @see Zend_Form
	 */
	require_once 'Zend/Form.php';
	
	
	class Bike_Form extends Zend_Form
	{
		static protected $_availableOptions = array();
		
		protected $_settings = array();

		public function init()
		{			parent::init();
			$this->addPrefixPath('Bike_Form', 'Bike/Form');
			$this->addElementPrefixPaths(array(
				Zend_Form_Element::VALIDATE => array(
					'prefix' => 'Bike_Validate',
					'path' => 'Bike/Validate',
				),
				Zend_Form_Element::FILTER => array(
					'prefix' => 'Bike_Filter',
					'path' => 'Bike/Filter',
				),
			));
		}

		public function getSettings($Name=null)
		{
			if(is_null($Name))
			{
				return (array) $this->_settings;
			}
			if($this->issetSetting($Name))
			{				return $this->_settings[$this->lowerCaseFirst($Name)];
			}
			return Null;
		}

		public function getSetting($Name)
		{
			return $this->getSettings($Name);
		}

		public function issetSetting($Name)
		{			return isset($this->_settings[$this->lowerCaseFirst($Name)]);
		}

		public function unsetSetting($Name)
		{
			unset($this->_settings[$this->lowerCaseFirst($Name)]);
		}

		public function setSetting($Name, $Value)
		{
			$this->_settings[$this->lowerCaseFirst($Name)] = $Value;
			return $this;
		}

		public function setSettings(array $Settings=null)
		{
			$this->_settings = array_merge((array)$this->_settings, (array)$Settings);
			return $this;
		}

		public function render(Zend_View_Interface $view = null, $renderType=Bike_Form_Renderer_Standard::TYPE)
		{			return Bike_Form_Renderer::factory($this, $renderType)->render($view);
		}

		public function standardRender()
		{			$args = func_get_args();
			return call_user_func_array(array(parent, 'render'), $args);
		}
		
		protected function getAvailableOptions($className)
		{
			if(!isset(self::$_availableOptions[$className]))
			{
				$reflection = new Zend_Reflection_Class($className);
				$options = array();
				$prefix = 'set';
				$len = strlen($prefix);
				foreach($reflection->getMethods() as $reflectionMethod)
				{
					$methodName = $reflectionMethod->getName();
					if(substr($methodName, 0, $len) == $prefix && strlen($methodName) > $len)
					{
						array_push($options, substr($methodName, $len));
					}
				}
				self::$_availableOptions[$className] = (array) $options;				
			}
			return self::$_availableOptions[$className];
		}

		public function createElement($Type, $Name, $Options = null)
		{
			$bikeAvailableOptions = $this->getAvailableOptions('Bike_Form_Element');
			$bikeOptions = array('Element' => null);
			foreach((array) $Options as $key => $value)
			{
				if(in_array(ucfirst($key), $bikeAvailableOptions))
				{
					$bikeOptions[$key] = $value;
					unset($Options[$key]);
				}				
			}
			$bikeOptions['Element'] = parent::createElement($Type, $Name, $Options);
			
			return new Bike_Form_Element($bikeOptions);
		}

		public function addElement($element, $name = null, $options = null)
	    {
	        if (is_string($element)) {
	            if (null === $name) {
	                require_once 'Zend/Form/Exception.php';
	                throw new Zend_Form_Exception('Elements specified by string must have an accompanying name');
	            }

	            if (is_array($this->_elementDecorators)) {
	                if (null === $options) {
	                    $options = array('decorators' => $this->_elementDecorators);
	                } elseif ($options instanceof Zend_Config) {
	                    $options = $options->toArray();
	                }
	                if (is_array($options)
	                    && !array_key_exists('decorators', $options)
	                ) {
	                    $options['decorators'] = $this->_elementDecorators;
	                }
	            }

	            $this->_elements[$name] = $this->createElement($element, $name, $options);
	        } elseif ($element instanceof Bike_Form_Element) {
	            $prefixPaths              = array();
	            $prefixPaths['decorator'] = $this->getPluginLoader('decorator')->getPaths();
	            if (!empty($this->_elementPrefixPaths)) {
	                $prefixPaths = array_merge($prefixPaths, $this->_elementPrefixPaths);
	            }

	            if (null === $name) {
	                $name = $element->getName();
	            }

	            $this->_elements[$name] = $element;
	            $this->_elements[$name]->addPrefixPaths($prefixPaths);
	        } else {
	            require_once 'Zend/Form/Exception.php';
	            throw new Zend_Form_Exception('Element must be specified by string or Zend_Form_Element instance');
	        }

	        $this->_order[$name] = $this->_elements[$name]->getOrder();
	        $this->_orderUpdated = true;
	        $this->_setElementsBelongTo($name);

	        return $this;
	    }

	    protected function lowerCaseFirst($Name)
		{
			return Bike_String::factory($Name)->lowerCaseFirst();
		}

	    public function __set($Name, $Value)
	    {
	        try
	        {	        	return parent::__set($Name, $Value);
	        }
	        catch(Zend_Form_Exception $e)
	        {
	        	$methodName = 'set'.ucfirst($Name);
	        	return $this->$methodName($Value);
			}
	    }

	    public function __get($Name)
	    {
	        $result = parent::__get($Name);
	        if(!is_null($result))
	        {	        	return $result;
	        }
	        $methodName = 'get'.ucfirst($Name);
		    return $this->$methodName();
	    }

	    public function __call($MethodName, array $Args=null)
	    {	    	try
	    	{	    		return parent::__call($MethodName, $Args);
	    	}
	    	catch(Zend_Form_Exception $e)
	    	{                if(substr($MethodName, 0, 3) == 'get')
	    		{
	    			$Name = substr($MethodName, 3);
	    			$Value = $this->getSetting($Name);
	    			if(method_exists($this, 'processGet'.$Name))
			        {
			        	$Value = call_user_func(array($this, 'processGet'.$Name), $Value);
			        }
	    			return $Value;
	    		}
	    		elseif(substr($MethodName, 0, 5) == 'unset')
	    		{
	    			$Name = substr($MethodName, 5);
	    			return $this->unsetSetting($Name);
	    		}
	    		elseif(substr($MethodName, 0, 5) == 'isset')
	    		{
	    			$Name = substr($MethodName, 5);
	    			return $this->issetSetting($Name);
	    		}
	    		elseif(substr($MethodName, 0, 3) == 'set')
	    		{
	    			$Name = substr($MethodName, 3);
	    			if(sizeof($Args) <= 1)
	    			{
	    				$Value = reset($Args);
	    			}
	    			else
	    			{
	    				$Value = $Args;
	    			}
	    			if(method_exists($this, 'processSet'.$Name))
			        {
			        	$value = call_user_func(array($this, 'processSet'.$Name), $Value);
			        }
			        else
			        {
			        	$value = $Value;
			        }
	    			$this->setSetting($Name, $value);
	    			return $this;
	    		}
	    	}
	    }

	    public function getMessagesArr()
	    {	    	$messages = array();
	    	foreach($this->getMessages() as $fieldName => $messages)
			{
				$messages[$fieldName] = array_values($messages);
			}
			return $messages;
	    }
	    
	    public function fixOrderDuplicates()
	    {
	    	$order = $this->_order;
	    	$i = 0;
	    	foreach($order as $key => $value)
	    	{
	    		$i++;
	    		if(is_null($value))
	    		{
	    			$order[$key] = $i;
	    		}
	    	}
	    	asort($order);
	    	$order = array_keys($order);
	    	$this->_order = array();
	    	$index = 0;
	    	foreach($order as $value)
	    	{
	    		$index++;
	    		$this->_order[$value] = $index;
	    	}
	    }
	}
